package com.aplink.generic.util;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

import com.aplink.generic.constant.ConfigParameter;

public class RotateImageView extends ImageView {
    private static final int ANIMATION_SPEED = 180 * 2;

    private static final int NONE = 0;
    private int beginAngle = 0;
    Path clipPath = null;
    private int deltaX = 0;

    private int deltaY = 0;
    private boolean editEnable = true;
    private boolean isMoved = false;
    private boolean isRequestEffect = false;
    private int lastDeltaX = 0;
    private int lastDeltaY = 0;
    // private ArrayList touchPoints = null;
    // private boolean isMultiTouch = false;
    // private int pathEffectPhase = 0;
    // private PointF centerPoint;
    private PointF lastPoint;
    private PointF lastPointPress;
    private final PointF lastPointPressSwipe = new PointF();
    private int lastTouchDegree = -1;
    private long mAnimationEndTime = 0L;
    private long mAnimationStartTime = 0L;
    private final Matrix matrix = new Matrix();
    private final float maxScaleZoom = 5f;

    private boolean mClockwise = false;
    private int mCurrentDegree = 0;

    private final float minScale = 1f;
    private final int mode = RotateImageView.NONE;

    private int mStartDegree = 0;
    private int mTargetDegree = 0;

    private float oldDist = 0.0f;
    public boolean onLeftSide = false, onTopSide = false, onRightSide = false,
            onBottomSide = false;

    private float origWidth, origHeight;

    private float saveScale = 1f;
    private float scaleFactor = 1.0f;
    private int scaleH = -1;
    private int scaleW = -1;
    private final boolean swipeEnable = false;

    private boolean useFitBoundMode = false;
    private boolean useRotateMode = true;
    private boolean useZoomMode = true;
    int w, h;
    private float width, height;

    //
    // private CreateNewKard parentActivity = null;
    // private CreateNewKard parentActivitySwipe = null;

    public RotateImageView(final Context context) {
        super(context);
        super.setClickable(true);
        initialize(context);
    }

    public RotateImageView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        initialize(context);
        super.setClickable(true);
    }

    public RotateImageView(final Context context, final AttributeSet attrs,
            final int defStyle) {
        super(context, attrs, defStyle);
        initialize(context);
        super.setClickable(true);
    }

    public RotateImageView(final Context context, final int defStyle) {
        super(context, null, defStyle);
        initialize(context);
        super.setClickable(true);
    }

    public void doRotate(final int degree) {
        setDegree(degree);
    }

    private int getDeltaX(final MotionEvent event) {
        return Math.round(event.getX(0) - this.lastPointPress.x);
    }

    private int getDeltaY(final MotionEvent event) {
        return Math.round(event.getY(0) - this.lastPointPress.y);
    }

    public int getImageHeight() {
        return this.scaleH;
    }

    public int getImageWidth() {
        return this.scaleW;
    }

    public int getImageX() {
        return this.lastDeltaX != -1 ? this.lastDeltaX : 0;
    }

    public int getImageY() {
        return this.lastDeltaY != -1 ? this.lastDeltaY : 0;
    }

    public boolean getSwipeEnable() {
        return this.swipeEnable;
    }

    private void initialize(final Context context) {
        // touchPoints = new ArrayList();
        this.lastPoint = new PointF();
        this.lastPoint.set(-1F, -1F);
        this.lastPointPress = new PointF();
        this.lastPointPress.set(-1F, -1F);
        this.editEnable = true;
    }

    // private void setPoints(MotionEvent event) {
    // touchPoints.clear();
    // int pointerIndex = 0;
    // for (int index = 0; index < event.getPointerCount(); index++) {
    // pointerIndex = event.getPointerId(index);
    // touchPoints.add(new PointF(event.getX(pointerIndex), event
    // .getY(pointerIndex)));
    // }
    //
    // }

    @Override
    protected void onDraw(final Canvas canvas) {
        final Drawable drawable = getDrawable();
        if (drawable == null) {
            return;
        }
        if (this.isRequestEffect && (drawable != null)) {
            this.isRequestEffect = false;
        }

        final Rect bounds2 = drawable.getBounds();
        if (this.scaleW == -1) {
            this.scaleW = getWidth();
            this.scaleH = Math
                    .round((getWidth() * (bounds2.bottom + bounds2.top))
                            / (bounds2.right + bounds2.left));
            if (getHeight() > this.scaleH) {
                this.scaleH = getHeight();
                this.scaleW = Math
                        .round((getHeight() * (bounds2.right + bounds2.left))
                                / (bounds2.bottom + bounds2.top));
            }
        }
        // end
        final Rect bounds = drawable.getBounds();

        this.w = Math.round(this.scaleFactor * (bounds.left + this.scaleW));
        this.h = Math.round(this.scaleFactor * (bounds.top + this.scaleH));
        drawable.setBounds(bounds.left, bounds.top, this.w, this.h);
        if ((this.w == 0) || (this.h == 0)) {
            return;
        }
        if (this.mCurrentDegree != this.mTargetDegree) {
            final long time = AnimationUtils.currentAnimationTimeMillis();
            if (time < this.mAnimationEndTime) {
                final int deltaTime = (int) (time - this.mAnimationStartTime);
                int degree = this.mStartDegree
                        + ((RotateImageView.ANIMATION_SPEED * (this.mClockwise ? deltaTime
                                : -deltaTime)) / 1000);
                degree = degree < 0 ? (degree % 360) + 360 : degree % 360;
                this.mCurrentDegree = degree;
                this.invalidate();
            } else {
                this.mCurrentDegree = this.mTargetDegree;
            }
        }
        final int left = getPaddingLeft();
        final int top = getPaddingTop();
        final int right = getPaddingRight();
        final int bottom = getPaddingBottom();
        final int width = getWidth() - left - right;
        final int height = getHeight() - top - bottom;
        final int saveCount = canvas.getSaveCount();
        canvas.translate(left + (width / 2) + this.deltaX, top + (height / 2)
                + this.deltaY);
        canvas.rotate(-this.mCurrentDegree);
        canvas.translate(-this.w >> 1, -this.h >> 1);
        drawable.draw(canvas);
        canvas.restoreToCount(saveCount);
    }

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        super.onTouchEvent(event);
        if (this.swipeEnable) {
            final int action = event.getAction() & MotionEvent.ACTION_MASK;
            switch (action) {
            case MotionEvent.ACTION_DOWN:
                this.lastPointPressSwipe.set(event.getX(0), event.getY(0));
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                break;

            case MotionEvent.ACTION_POINTER_UP:
                break;

            case MotionEvent.ACTION_UP:
                if ((this.lastPointPressSwipe.x != -1) && this.isMoved
                        && (event.getX(0) > (this.lastPointPressSwipe.x - 30))) {
                    // parentActivitySwipe.swipeLeftTemplate();
                }
                if ((this.lastPointPressSwipe.x != -1) && this.isMoved
                        && (event.getX(0) < (this.lastPointPressSwipe.x - 30))) {
                    // parentActivitySwipe.swipeRightTemplate();
                }
                this.isMoved = false;
                this.lastPointPressSwipe.set(-1F, -1F);
                break;

            case MotionEvent.ACTION_MOVE:
                this.isMoved = true;
                break;
            }
            return true;
        }
        if (!this.editEnable) {
            // TCuong 07 - 12 - 2012 return touch event for parent view - fix
            // bug KB not hide
            return false;
        }
        // end
        final int action = event.getAction() & MotionEvent.ACTION_MASK;
        try {
            switch (action) {
            case MotionEvent.ACTION_DOWN:
                this.lastPointPress.set(event.getX(0), event.getY(0));
                this.invalidate();
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                // lastPointPress.set(event.getX(1), event.getY(1));
                // isMultiTouch = true;
                this.beginAngle = rotation(event);
                this.oldDist = spacing(event);
                this.invalidate();
                break;

            case MotionEvent.ACTION_POINTER_UP:
                // isMultiTouch = false;
                this.lastPoint.set(-1F, -1F);
                // lastDeltaX = deltaX;
                // lastDeltaY = deltaY;
                this.lastPointPress.set(-1F, -1F);
                if (this.scaleW > -1) {
                    if (this.scaleW > this.scaleH) {
                        this.scaleW = Math
                                .round(this.scaleFactor * this.scaleH) > 10 ? Math
                                .round(this.scaleFactor * this.scaleW)
                                : (10 * getWidth()) / getHeight();
                        this.scaleH = Math
                                .round(this.scaleFactor * this.scaleH) > 10 ? Math
                                .round(this.scaleFactor * this.scaleH) : 10;
                    } else {
                        this.scaleW = Math
                                .round(this.scaleFactor * this.scaleW) > 10 ? Math
                                .round(this.scaleFactor * this.scaleW) : 10;
                        this.scaleH = Math
                                .round(this.scaleFactor * this.scaleW) > 10 ? Math
                                .round(this.scaleFactor * this.scaleH)
                                : (10 * getHeight()) / getWidth();
                    }
                }
                this.scaleFactor = 1.0F;
                this.lastTouchDegree = this.mTargetDegree;
                // lastDeltaX = deltaX;
                // lastDeltaY = deltaY;
                // lastTouchDegree += mCurrentDegree;
                break;

            case MotionEvent.ACTION_UP:
                // isMultiTouch = false;
                this.lastPoint.set(-1F, -1F);
                this.lastPointPress.set(-1F, -1F);
                if (this.scaleW > -1) {
                    if (this.scaleW > this.scaleH) {
                        this.scaleW = Math
                                .round(this.scaleFactor * this.scaleH) > 10 ? Math
                                .round(this.scaleFactor * this.scaleW)
                                : (10 * getWidth()) / getHeight();
                        this.scaleH = Math
                                .round(this.scaleFactor * this.scaleH) > 10 ? Math
                                .round(this.scaleFactor * this.scaleH) : 10;
                    } else {
                        this.scaleW = Math
                                .round(this.scaleFactor * this.scaleW) > 10 ? Math
                                .round(this.scaleFactor * this.scaleW) : 10;
                        this.scaleH = Math
                                .round(this.scaleFactor * this.scaleW) > 10 ? Math
                                .round(this.scaleFactor * this.scaleH)
                                : (10 * getHeight()) / getWidth();
                    }
                }
                this.scaleFactor = 1.0F;
                this.lastTouchDegree = this.mTargetDegree;
                this.lastDeltaX = this.deltaX;
                this.lastDeltaY = this.deltaY;
                // lastTouchDegree += mCurrentDegree;
                break;

            case MotionEvent.ACTION_MOVE:
                if (event.getPointerCount() > 1) {
                    if (this.lastPoint.x == -1F) {
                        this.lastPoint.set(
                                Math.abs(event.getX(0) - event.getX(1)),
                                Math.abs(event.getY(0) - event.getY(1)));
                    } else {
                        final int width = getWidth() - getPaddingLeft()
                                - getPaddingRight();
                        final int height = getHeight() - getPaddingTop()
                                - getPaddingBottom();
                        if (((((width / 2) + Math.abs(this.deltaX)) <= (Math
                                .round((spacing(event) / this.oldDist)
                                        * (getDrawable().getBounds().left + this.scaleW)) >> 1)) && (((height / 2) + Math
                                .abs(this.deltaY)) <= (Math
                                .round((spacing(event) / this.oldDist)
                                        * (getDrawable().getBounds().top + this.scaleH)) >> 1)))
                                || !this.useFitBoundMode) {
                            if (this.useZoomMode) {
                                this.scaleFactor = spacing(event)
                                        / this.oldDist;
                            }
                        }
                        // Log.d("debug degree lastTouchDegree", "  " +
                        // lastTouchDegree);
                        // Log.d("debug degree angle", "  " + (beginAngle -
                        // rotation(event)));
                        // Log.d("debug degree", "  " + (beginAngle -
                        // rotation(event) + lastTouchDegree));
                        // if(Math.abs(beginAngle - rotation(event)) < 180);
                        if (this.useRotateMode) {
                            setDegree((this.beginAngle - rotation(event))
                                    + this.lastTouchDegree);// >
                            // 10
                            // ?
                            // rotation(event)
                            // + mCurrentDegree
                            // :
                            // mCurrentDegree);
                        }
                    }
                } else {
                    if (this.lastPointPress.x == -1F) {
                        this.lastPointPress.set(event.getX(0), event.getY(0));
                    } else {
                        final int width = getWidth() - getPaddingLeft()
                                - getPaddingRight();
                        final int height = getHeight() - getPaddingTop()
                                - getPaddingBottom();
                        if ((((width / 2) + Math.abs(getDeltaX(event)
                                + this.lastDeltaX)) <= (this.w >> 1))
                                || !this.useFitBoundMode) {
                            this.deltaX = getDeltaX(event) + this.lastDeltaX;
                        }
                        if ((((height / 2) + Math.abs(getDeltaY(event)
                                + this.lastDeltaY)) <= (this.h >> 1))
                                || !this.useFitBoundMode) {
                            this.deltaY = getDeltaY(event) + this.lastDeltaY;
                        }
                    }
                }
                this.invalidate();
                break;
            }
        } catch (final Exception exception) {
        }
        return true;
    }

    public void onZoom(final boolean zoomIn) {

        float mScaleFactor = zoomIn ? 1.2f : 0.75f;

        final float origScale = this.saveScale;
        this.saveScale *= mScaleFactor;

        if (this.saveScale > this.maxScaleZoom) {
            this.saveScale = this.maxScaleZoom;
            mScaleFactor = this.maxScaleZoom / origScale;
        } else if (this.saveScale < this.minScale) {
            this.saveScale = this.minScale;
            mScaleFactor = this.minScale / origScale;
        }

        if (((this.origWidth * this.saveScale) <= this.width)
                || ((this.origHeight * this.saveScale) <= this.height)) {
            this.matrix.postScale(mScaleFactor, mScaleFactor, this.width / 2,
                    this.height / 2);
        } else {
            this.matrix.postScale(mScaleFactor, mScaleFactor, this.width / 2,
                    this.height / 2);
        }

        // matrix.getValues(m);
        // calcPadding();
        // checkAndSetTranslate(0, 0);

        // Log.w(TAG, "right : " + right);
        // Log.w(TAG, "width : " + width);
        // Log.w(TAG, "bottom : " + bottom);
        // Log.w(TAG, "height : " + height);
        // Log.w(TAG, "redundantXSpace : " + redundantXSpace);
        // Log.w(TAG, "redundantYSpace : " + redundantYSpace);

        setImageMatrix(this.matrix);
        this.invalidate();
    }

    public boolean pagerCanScroll() {
        if (this.mode != RotateImageView.NONE) {
            return false;
        }
        return this.saveScale == this.minScale;
    }

    public void reset() {
        // TCuong 10 - 12 - 2012 fix scale image input from camera or local file
        this.scaleW = -1;// getWidth();
        this.scaleH = -1;// getHeight();
        // end
        setDegree(0);
        this.scaleFactor = 1.0F;
        this.lastTouchDegree = 0;
        this.lastDeltaX = 0;
        this.lastDeltaY = 0;
        this.deltaX = 0;
        this.deltaY = 0;
    }

    private int rotation(final MotionEvent event) {
        final double delta_x = event.getX(0) - event.getX(1);
        final double delta_y = event.getY(0) - event.getY(1);

        if (delta_x == 0) {
            return 0;
        } else {
            double radians;
            // if (delta_x > 0){
            radians = Math.atan2(delta_y, delta_x);
            return (int) Math.round(Math.toDegrees(radians));
            // }
            // else{
            // radians = Math.atan2(delta_y, -delta_x);
            // return 180 + (int) Math.round(Math.toDegrees(radians));
            // }
        }
    }

    public void setDefaultDrawing(final boolean enable) {
    }

    protected void setDegree(int degree) {
        // make sure in the range of [0, 359]
        degree = degree >= 0 ? degree % 360 : (degree % 360) + 360;
        if (degree == this.mTargetDegree) {
            return;
        }

        this.mTargetDegree = degree;
        this.mStartDegree = this.mCurrentDegree;
        this.mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis();

        int diff = this.mTargetDegree - this.mCurrentDegree;
        diff = diff >= 0 ? diff : 360 + diff; // make it in range [0, 359]

        // Make it in range [-179, 180]. That's the shorted distance between the
        // two angles
        diff = diff > 180 ? diff - 360 : diff;

        this.mClockwise = diff >= 0;
        this.mAnimationEndTime = this.mAnimationStartTime
                + ((Math.abs(diff) * 1000) / RotateImageView.ANIMATION_SPEED);

        this.invalidate();
        System.out.println(">>>> degree = " + degree);
        ConfigParameter.DEGREES_ROTATION = degree;
    }

    public void setEdit(final boolean enable) {
        this.editEnable = enable;
    }

    public void setImageHeight(final int h) {
        this.scaleH = h;
    }

    public void setImageWidth(final int w) {
        this.scaleW = w;
    }

    public void setImageX(final int x) {
        this.deltaX = x;
        this.lastDeltaX = x;
    }

    public void setImageY(final int y) {
        this.deltaY = y;
        this.lastDeltaY = y;
    }

    public void setUseFitBoundMode(final boolean values) {
        this.useFitBoundMode = values;
    }

    public void setUseRotateMode(final boolean values) {
        this.useRotateMode = values;
    }

    public void setUseRoundCorner(final boolean values) {
    }

    public void setUseZoomMode(final boolean values) {
        this.useZoomMode = values;
    }

    // public void requestEffect(CreateNewKard instance) {
    // isRequestEffect = true;
    // parentActivity = instance;
    // }

    private float spacing(final MotionEvent event) {
        final float x = event.getX(0) - event.getX(1);
        final float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt((x * x) + (y * y));
    }

}
